<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  

  
  <title>一篇文章搞懂promise | 蔡进东的日志</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="Promise 解决了什么问题？在传统的异步编程中，如果异步之间存在依赖关系，就需要通过层层嵌套回调的方式满足这种依赖，如果嵌套层数过多，可读性和可以维护性都会变得很差，产生所谓的“回调地狱”。而 Promise 将嵌套调用改为链式调用，增加了可阅读性和可维护性。也就是说，Promise 用一种更加友好的代码组织方式解决了异步嵌套问题。Promise的缺陷是无法中断请求。产生回调地狱的原因主要有两">
<meta name="keywords" content="promise">
<meta property="og:type" content="article">
<meta property="og:title" content="一篇文章搞懂promise">
<meta property="og:url" content="http://cai4633.github.io/2020/08/18/一篇文章搞懂promise/index.html">
<meta property="og:site_name" content="蔡进东的日志">
<meta property="og:description" content="Promise 解决了什么问题？在传统的异步编程中，如果异步之间存在依赖关系，就需要通过层层嵌套回调的方式满足这种依赖，如果嵌套层数过多，可读性和可以维护性都会变得很差，产生所谓的“回调地狱”。而 Promise 将嵌套调用改为链式调用，增加了可阅读性和可维护性。也就是说，Promise 用一种更加友好的代码组织方式解决了异步嵌套问题。Promise的缺陷是无法中断请求。产生回调地狱的原因主要有两">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2021-01-03T10:18:44.938Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="一篇文章搞懂promise">
<meta name="twitter:description" content="Promise 解决了什么问题？在传统的异步编程中，如果异步之间存在依赖关系，就需要通过层层嵌套回调的方式满足这种依赖，如果嵌套层数过多，可读性和可以维护性都会变得很差，产生所谓的“回调地狱”。而 Promise 将嵌套调用改为链式调用，增加了可阅读性和可维护性。也就是说，Promise 用一种更加友好的代码组织方式解决了异步嵌套问题。Promise的缺陷是无法中断请求。产生回调地狱的原因主要有两">
  
  
    <link rel="icon" href="/favicon.png">
  
  
    <link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
  
  <link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
  <div id="container">
    <div id="wrap">
      <header id="header">
  <div id="banner"></div>
  <div id="header-outer" class="outer">
    <div id="header-title" class="inner">
      <h1 id="logo-wrap">
        <a href="/" id="logo">蔡进东的日志</a>
      </h1>
      
      <h2 id="subtitle-wrap">
        <a href="/" id="subtitle">不要在最该奋斗的年纪选择去偷懒，只有度过一段连自己都被感动了的日子，才能变成那个最好的自己.</a>
      </h2>
      
    </div>
    <div id="header-inner" class="inner">
      <nav id="main-nav">
        <a id="main-nav-toggle" class="nav-icon"></a>
        
        <a class="main-nav-link" href="/">Home</a>
        
        <a class="main-nav-link" href="/archives">Archives</a>
        
      </nav>
      <nav id="sub-nav">
        
        <a id="nav-search-btn" class="nav-icon" title="搜索"></a>
      </nav>
      <div class="local-search local-search-plugin">
        <input type="search" placeholder="站内搜索" id="local-search-input" class="local-search-input-cls" />
        <div id="local-search-result" class="local-search-result-cls"></div>
      </div>
      <!-- <div id="search-form-wrap">
        <form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit">&#xF002;</button><input type="hidden" name="sitesearch" value="http://cai4633.github.io"></form>
      </div> -->
    </div>
  </div>
</header>

      <div class="outer">
        <section id="main"><article id="post-一篇文章搞懂promise" class="article article-type-post" itemscope itemprop="blogPost">
  <div class="article-meta">
    <a href="/2020/08/18/一篇文章搞懂promise/" class="article-date">
  <time datetime="2020-08-18T12:24:25.000Z" itemprop="datePublished">2020-08-18</time>
</a>
    
  <div class="article-category">
    <a class="article-category-link" href="/categories/js/">js</a>
  </div>

  </div>
  <div class="article-inner">
    
    
      <header class="article-header">
        
  
    <h1 class="article-title" itemprop="name">
      一篇文章搞懂promise
    </h1>
  

      </header>
    
    <div class="article-entry" itemprop="articleBody">
      
        <h2 id="Promise-解决了什么问题？"><a href="#Promise-解决了什么问题？" class="headerlink" title="Promise 解决了什么问题？"></a>Promise 解决了什么问题？</h2><p>在传统的异步编程中，如果异步之间存在依赖关系，就需要通过层层嵌套回调的方式满足这种依赖，如果嵌套层数过多，可读性和可以维护性都会变得很差，产生所谓的<strong>“回调地狱”</strong>。而 <code>Promise</code> 将嵌套调用改为<strong>链式调用</strong>，增加了可阅读性和可维护性。也就是说，Promise 用一种更加<strong>友好的代码组织方式</strong>解决了<strong>异步嵌套</strong>问题。Promise的缺陷是无法中断请求。<br>产生回调地狱的原因主要有两点：</p>
<ol>
<li>(异步请求)嵌套调用，第一个函数的输出往往是第二个函数的输入；(Promise thenable 链式调用可以解决)</li>
<li>处理多个异步请求并发，开发时需要同步请求最终的结果。（Promise.all()可以解决）</li>
</ol>
<h2 id="then-的链式调用和值穿透"><a href="#then-的链式调用和值穿透" class="headerlink" title="then 的链式调用和值穿透"></a>then 的链式调用和值穿透</h2><p>在我们使用 Promise 的时候，当 then 函数中 return 了一个值，不管是什么值，我们都能在下一个 then 中获取到，这就是所谓的 then 的链式调用。而且，当我们不在 then 中放入参数，例：promise.then().then()，那么其后面的 then 依旧可以得到之前 then 返回的值，这就是所谓的值的穿透。</p>
<h2 id="Promise-的-API"><a href="#Promise-的-API" class="headerlink" title="Promise 的 API"></a>Promise 的 API</h2><ul>
<li>Promise.resolve() 产生一个成功的 promise, 直接将值变成成功结果</li>
<li>Promise.reject()  产生一个失败的 promise，直接将值变成错误结果</li>
<li>Promise.all()   解决并发问题的，多个异步并发获取最终的结果（如果有一个失败则失败)</li>
<li>Promise.race()  处理多个请求,谁先完成用谁的</li>
<li>Promise.prototype.catch()   捕获 promise 的异常</li>
<li>Promise.prototype.finally()  无论如何都会执行</li>
</ul>
<h2 id="Promise-A-规范"><a href="#Promise-A-规范" class="headerlink" title="Promise/A+ 规范"></a>Promise/A+ 规范</h2><ol>
<li>promise 有三个状态：<strong>pending，fulfilled，rejected</strong>, 默认状态是 pending</li>
<li>new promise 时， 需要传递一个 executor(resolve, reject)执行器，<strong>执行器立即执行</strong></li>
<li>promise 有一个<strong>value 保存成功状态</strong>的值，可以是 undefined/thenable/promise</li>
<li>promise 有一个<strong>reason 保存失败状态</strong>的值</li>
<li>promise <strong>只能</strong>从 pending 到 rejected, 或者从 pending 到 fulfilled，状态一旦确认，就不会再改变</li>
<li>promise 必须有一个 then 方法，then 接收两个参数，分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected</li>
<li>如果调用 then 时，promise 已经成功，则执行 onFulfilled，参数是 promise 的 value；</li>
<li>如果调用 then 时，promise 已经失败，那么执行 onRejected, 参数是 promise 的 reason；</li>
<li>如果 then 中抛出了异常，那么就会把这个异常作为参数，传递给下一个 then 的失败的回调 onRejected</li>
<li>then 的参数 onFulfilled 和 onRejected 可以缺省，如果 onFulfilled 或者 onRejected 不是函数，将其忽略，且依旧可以在下面的 then 中获取到之前返回的值；</li>
<li>promise 可以 then 多次，每次执行完 promise.then 方法后返回的都是一个<strong>“新的 promise”</strong></li>
<li>如果 then 的返回值是一个普通值，那么就会把这个结果作为参数，传递给下一个 then 的成功的回调中；</li>
<li>如果 then 的<strong>返回值是一个 promise，那么会等这个 promise 执行完</strong>，promise 如果成功，就走下一个 then 的成功；如果失败，就走下一个 then 的失败；如果抛 1. 出异常，就走下一个 then 的失败</li>
<li>如果 then 的返回值和 promise 是同一个引用对象，造成循环引用，则抛出异常，把异常传递给下一个 then 的失败的回调中</li>
<li>如果 then 的返回值 x 是一个 promise，且 x 同时调用 resolve 函数和 reject 函数，则第一次调用优先，其他所有调用被忽略</li>
</ol>
<p><code>Promise实现代码：</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br></pre></td><td class="code"><pre><span class="line">const PENDING = &apos;PENDING&apos;;</span><br><span class="line">const FULFILLED = &apos;FULFILLED&apos;;</span><br><span class="line">const REJECTED = &apos;REJECTED&apos;;</span><br><span class="line"></span><br><span class="line">const resolvePromise = (promise2, x, resolve, reject) =&gt; &#123;</span><br><span class="line">  // 自己等待自己完成是错误的实现，用一个类型错误，结束掉 promise  Promise/A+ 2.3.</span><br><span class="line">  if (promise2 === x) &#123;</span><br><span class="line">    return reject(new TypeError(&apos;Chaining cycle detected for promise #&lt;Promise&gt;&apos;))</span><br><span class="line">  &#125;</span><br><span class="line">  // Promise/A+ 2.3.3.3.3 只能调用一次</span><br><span class="line">  let called;</span><br><span class="line">  // 后续的条件要严格判断 保证代码能和别的库一起使用</span><br><span class="line">  if ((typeof x === &apos;object&apos; &amp;&amp; x != null) || typeof x === &apos;function&apos;) &#123;</span><br><span class="line">    try &#123;</span><br><span class="line">      // 为了判断 resolve 过的就不用再 reject 了（比如 reject 和 resolve 同时调用的时候）  Promise/A+ 2.3.3.1</span><br><span class="line">      let then = x.then;</span><br><span class="line">      if (typeof then === &apos;function&apos;) &#123;</span><br><span class="line">        // 不要写成 x.then，直接 then.call 就可以了 因为 x.then 会再次取值，Object.defineProperty  Promise/A+ 2.3.3.3</span><br><span class="line">        then.call(x, y =&gt; &#123; // 根据 promise 的状态决定是成功还是失败</span><br><span class="line">          if (called) return;</span><br><span class="line">          called = true;</span><br><span class="line">          // 递归解析的过程（因为可能 promise 中还有 promise） Promise/A+ 2.3.3.3.1</span><br><span class="line">          resolvePromise(promise2, y, resolve, reject);</span><br><span class="line">        &#125;, r =&gt; &#123;</span><br><span class="line">          // 只要失败就失败 Promise/A+ 2.3.3.3.2</span><br><span class="line">          if (called) return;</span><br><span class="line">          called = true;</span><br><span class="line">          reject(r);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125; else &#123;</span><br><span class="line">        // 如果 x.then 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.3.4</span><br><span class="line">        resolve(x);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125; catch (e) &#123;</span><br><span class="line">      // Promise/A+ 2.3.3.2</span><br><span class="line">      if (called) return;</span><br><span class="line">      called = true;</span><br><span class="line">      reject(e)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125; else &#123;</span><br><span class="line">    // 如果 x 是个普通值就直接返回 resolve 作为结果  Promise/A+ 2.3.4</span><br><span class="line">    resolve(x)</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">class Promise &#123;</span><br><span class="line">  constructor(executor) &#123;</span><br><span class="line">    this.status = PENDING;</span><br><span class="line">    this.value = undefined;</span><br><span class="line">    this.reason = undefined;</span><br><span class="line">    this.onResolvedCallbacks = [];</span><br><span class="line">    this.onRejectedCallbacks= [];</span><br><span class="line"></span><br><span class="line">    let resolve = (value) =&gt; &#123;</span><br><span class="line">      if(this.status ===  PENDING) &#123;</span><br><span class="line">        this.status = FULFILLED;</span><br><span class="line">        this.value = value;</span><br><span class="line">        this.onResolvedCallbacks.forEach(fn=&gt;fn());</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    let reject = (reason) =&gt; &#123;</span><br><span class="line">      if(this.status ===  PENDING) &#123;</span><br><span class="line">        this.status = REJECTED;</span><br><span class="line">        this.reason = reason;</span><br><span class="line">        this.onRejectedCallbacks.forEach(fn=&gt;fn());</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    try &#123;</span><br><span class="line">      executor(resolve,reject)</span><br><span class="line">    &#125; catch (error) &#123;</span><br><span class="line">      reject(error)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&lt;!-- 静态方法 --&gt;</span><br><span class="line">  static resolve(data)&#123;</span><br><span class="line">    return new Promise((resolve,reject)=&gt;&#123;</span><br><span class="line">      resolve(data);</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static reject(reason)&#123;</span><br><span class="line">    return new Promise((resolve,reject)=&gt;&#123;</span><br><span class="line">      reject(reason);</span><br><span class="line">    &#125;)</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static race(promises) &#123;</span><br><span class="line">    return new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      // 一起执行就是for循环</span><br><span class="line">      for (let i = 0; i &lt; promises.length; i++) &#123;</span><br><span class="line">        let val = promises[i];</span><br><span class="line">        if (val &amp;&amp; typeof val.then === &apos;function&apos;) &#123;</span><br><span class="line">          val.then(resolve, reject);</span><br><span class="line">        &#125; else &#123; // 普通值</span><br><span class="line">          resolve(val)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  static all(values) &#123;</span><br><span class="line">    if (!Array.isArray(values)) &#123;</span><br><span class="line">      const type = typeof values;</span><br><span class="line">      return new TypeError(`TypeError: $&#123;type&#125; $&#123;values&#125; is not iterable`)</span><br><span class="line">    &#125;</span><br><span class="line">    return new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      let resultArr = [];</span><br><span class="line">      let orderIndex = 0;</span><br><span class="line">      const processResultByKey = (value, index) =&gt; &#123;</span><br><span class="line">        resultArr[index] = value;</span><br><span class="line">        if (++orderIndex === values.length) &#123;</span><br><span class="line">            resolve(resultArr)</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">      for (let i = 0; i &lt; values.length; i++) &#123;</span><br><span class="line">        let value = values[i];</span><br><span class="line">        if (value &amp;&amp; typeof value.then === &apos;function&apos;) &#123;</span><br><span class="line">          value.then((value) =&gt; &#123;</span><br><span class="line">            processResultByKey(value, i);</span><br><span class="line">          &#125;, reject);</span><br><span class="line">        &#125; else &#123;</span><br><span class="line">          processResultByKey(value, i);</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  then(onFulfilled, onRejected) &#123;</span><br><span class="line">    //解决 onFufilled，onRejected 没有传值的问题</span><br><span class="line">    //Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4</span><br><span class="line">    onFulfilled = typeof onFulfilled === &apos;function&apos; ? onFulfilled : v =&gt; v;</span><br><span class="line">    //因为错误的值要让后面访问到，所以这里也要跑出个错误，不然会在之后 then 的 resolve 中捕获</span><br><span class="line">    onRejected = typeof onRejected === &apos;function&apos; ? onRejected : err =&gt; &#123; throw err &#125;;</span><br><span class="line">    // 每次调用 then 都返回一个新的 promise  Promise/A+ 2.2.7</span><br><span class="line">    let promise2 = new Promise((resolve, reject) =&gt; &#123;</span><br><span class="line">      if (this.status === FULFILLED) &#123;</span><br><span class="line">        //Promise/A+ 2.2.2</span><br><span class="line">        //Promise/A+ 2.2.4 --- setTimeout</span><br><span class="line">        setTimeout(() =&gt; &#123;</span><br><span class="line">          try &#123;</span><br><span class="line">            //Promise/A+ 2.2.7.1</span><br><span class="line">            let x = onFulfilled(this.value);</span><br><span class="line">            // x可能是一个proimise</span><br><span class="line">            resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">          &#125; catch (e) &#123;</span><br><span class="line">            //Promise/A+ 2.2.7.2</span><br><span class="line">            reject(e)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;, 0);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      if (this.status === REJECTED) &#123;</span><br><span class="line">        //Promise/A+ 2.2.3</span><br><span class="line">        setTimeout(() =&gt; &#123;</span><br><span class="line">          try &#123;</span><br><span class="line">            let x = onRejected(this.reason);</span><br><span class="line">            resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">          &#125; catch (e) &#123;</span><br><span class="line">            reject(e)</span><br><span class="line">          &#125;</span><br><span class="line">        &#125;, 0);</span><br><span class="line">      &#125;</span><br><span class="line"></span><br><span class="line">      if (this.status === PENDING) &#123;</span><br><span class="line">        this.onResolvedCallbacks.push(() =&gt; &#123;</span><br><span class="line">          setTimeout(() =&gt; &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">              let x = onFulfilled(this.value);</span><br><span class="line">              resolvePromise(promise2, x, resolve, reject);</span><br><span class="line">            &#125; catch (e) &#123;</span><br><span class="line">              reject(e)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;, 0);</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        this.onRejectedCallbacks.push(()=&gt; &#123;</span><br><span class="line">          setTimeout(() =&gt; &#123;</span><br><span class="line">            try &#123;</span><br><span class="line">              let x = onRejected(this.reason);</span><br><span class="line">              resolvePromise(promise2, x, resolve, reject)</span><br><span class="line">            &#125; catch (e) &#123;</span><br><span class="line">              reject(e)</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;, 0);</span><br><span class="line">        &#125;);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    return promise2;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Promise.prototype.catch = function(errCallback)&#123;</span><br><span class="line">  return this.then(null,errCallback)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Promise.prototype.finally = function(callback) &#123;</span><br><span class="line">  return this.then((value)=&gt;&#123;</span><br><span class="line">    return Promise.resolve(callback()).then(()=&gt;value)</span><br><span class="line">  &#125;,(reason)=&gt;&#123;</span><br><span class="line">    return Promise.resolve(callback()).then(()=&gt;&#123;throw reason&#125;)</span><br><span class="line">  &#125;)  </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://zhuanlan.zhihu.com/p/183801144" target="_blank" rel="noopener">你能手写一个 Promise 吗</a></p>

      
    </div>
    <footer class="article-footer">
      <!--<a data-url="http://cai4633.github.io/2020/08/18/一篇文章搞懂promise/" data-id="ckjqth4rz002i2gw03ckvb48w" class="article-share-link">Share</a>-->
      
      
  <ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/promise/">promise</a></li></ul>

    </footer>
  </div>
  <!---->
    <!--
<nav id="article-nav">
  
    <a href="/2020/09/18/虚拟DOM和DOM diff/" id="article-nav-newer" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Newer</strong>
      <div class="article-nav-title">
        
          虚拟 DOM 和 DOM diff
        
      </div>
    </a>
  
  
    <a href="/2020/07/18/防抖和节流/" id="article-nav-older" class="article-nav-link-wrap">
      <strong class="article-nav-caption">Older</strong>
      <div class="article-nav-title">防抖和节流</div>
    </a>
  
</nav>
-->
  <!---->
</article>

</section>
        
          <aside id="sidebar">
  
    
  <div class="widget-wrap">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget">
      <ul>
        
          <li>
            <a href="/2021/01/08/前端鉴权总结/">前端鉴权总结</a>
          </li>
        
          <li>
            <a href="/2021/01/03/web性能优化/">web性能优化</a>
          </li>
        
          <li>
            <a href="/2020/12/31/nodejs入门/">nodejs 入门</a>
          </li>
        
          <li>
            <a href="/2020/12/27/vue单页面应用白屏屏优化/">vue单页面应用白屏屏优化</a>
          </li>
        
          <li>
            <a href="/2020/12/26/逸辰音乐问题点/">逸辰音乐问题点</a>
          </li>
        
      </ul>
    </div>
  </div>

  
    
  <div class="widget-wrap">
    <h3 class="widget-title">归档</h3>
    <div class="widget">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2021/01/">一月 2021</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/12/">十二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/09/">九月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/08/">八月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/07/">七月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/05/">五月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/04/">四月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">三月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/02/">二月 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/11/">十一月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/10/">十月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">七月 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">六月 2019</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">分类</h3>
    <div class="widget">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/ES6/">ES6</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/Vue/">Vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/css/">css</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/html/">html</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/js/">js</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/nodejs/">nodejs</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/vue/">vue</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端/">前端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/前端项目/">前端项目</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/服务端/">服务端</a></li><li class="category-list-item"><a class="category-list-link" href="/categories/算法/">算法</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签</h3>
    <div class="widget">
      <ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/tags/Apache/">Apache</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Array/">Array</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/BFC/">BFC</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/CORS/">CORS</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM/">DOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/DOM-diff/">DOM diff</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/JSONP/">JSONP</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/Vue3/">Vue3</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/absolute/">absolute</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/ajax/">ajax</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/auto/">auto</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/background/">background</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/const/">const</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/devServer/">devServer</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/function/">function</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/git/">git</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/github/">github</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/hexo/">hexo</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/host/">host</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/html/">html</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/http/">http</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/jquery/">jquery</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/js/">js</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/leancloud/">leancloud</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/let/">let</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/nodejs/">nodejs</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/npm/">npm</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/position/">position</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/promise/">promise</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/proxy/">proxy</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/script标签/">script标签</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/session-cookie/">session-cookie</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/token/">token</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-if/">v-if</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/v-show/">v-show</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vDOM/">vDOM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vHost/">vHost</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue/">vue</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue-cli/">vue-cli</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vuec/">vuec</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/vue生命周期/">vue生命周期</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/width/">width</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/z-index/">z-index</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/原型链/">原型链</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/同源/">同源</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/外边距/">外边距</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/层叠上下文/">层叠上下文</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/性能优化/">性能优化</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/数据结构/">数据结构</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/浏览器/">浏览器</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/百分比/">百分比</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/算法/">算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/精华/">精华</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/软件下载/">软件下载</a></li><li class="tag-list-item"><a class="tag-list-link" href="/tags/鉴权/">鉴权</a></li></ul>
    </div>
  </div>


  
    
  <div class="widget-wrap">
    <h3 class="widget-title">标签云</h3>
    <div class="widget tagcloud">
      <a href="/tags/Apache/" style="font-size: 10px;">Apache</a> <a href="/tags/Array/" style="font-size: 10px;">Array</a> <a href="/tags/BFC/" style="font-size: 10px;">BFC</a> <a href="/tags/CORS/" style="font-size: 10px;">CORS</a> <a href="/tags/DOM/" style="font-size: 10px;">DOM</a> <a href="/tags/DOM-diff/" style="font-size: 10px;">DOM diff</a> <a href="/tags/JSONP/" style="font-size: 10px;">JSONP</a> <a href="/tags/Vue3/" style="font-size: 10px;">Vue3</a> <a href="/tags/absolute/" style="font-size: 10px;">absolute</a> <a href="/tags/ajax/" style="font-size: 10px;">ajax</a> <a href="/tags/auto/" style="font-size: 10px;">auto</a> <a href="/tags/background/" style="font-size: 10px;">background</a> <a href="/tags/const/" style="font-size: 10px;">const</a> <a href="/tags/devServer/" style="font-size: 10px;">devServer</a> <a href="/tags/function/" style="font-size: 10px;">function</a> <a href="/tags/git/" style="font-size: 10px;">git</a> <a href="/tags/github/" style="font-size: 10px;">github</a> <a href="/tags/hexo/" style="font-size: 10px;">hexo</a> <a href="/tags/host/" style="font-size: 10px;">host</a> <a href="/tags/html/" style="font-size: 10px;">html</a> <a href="/tags/http/" style="font-size: 16.67px;">http</a> <a href="/tags/jquery/" style="font-size: 10px;">jquery</a> <a href="/tags/js/" style="font-size: 20px;">js</a> <a href="/tags/leancloud/" style="font-size: 10px;">leancloud</a> <a href="/tags/let/" style="font-size: 10px;">let</a> <a href="/tags/nodejs/" style="font-size: 10px;">nodejs</a> <a href="/tags/npm/" style="font-size: 10px;">npm</a> <a href="/tags/position/" style="font-size: 10px;">position</a> <a href="/tags/promise/" style="font-size: 10px;">promise</a> <a href="/tags/proxy/" style="font-size: 10px;">proxy</a> <a href="/tags/script标签/" style="font-size: 10px;">script标签</a> <a href="/tags/session-cookie/" style="font-size: 10px;">session-cookie</a> <a href="/tags/token/" style="font-size: 10px;">token</a> <a href="/tags/v-if/" style="font-size: 10px;">v-if</a> <a href="/tags/v-show/" style="font-size: 10px;">v-show</a> <a href="/tags/vDOM/" style="font-size: 10px;">vDOM</a> <a href="/tags/vHost/" style="font-size: 10px;">vHost</a> <a href="/tags/vue/" style="font-size: 13.33px;">vue</a> <a href="/tags/vue-cli/" style="font-size: 10px;">vue-cli</a> <a href="/tags/vuec/" style="font-size: 10px;">vuec</a> <a href="/tags/vue生命周期/" style="font-size: 10px;">vue生命周期</a> <a href="/tags/width/" style="font-size: 10px;">width</a> <a href="/tags/z-index/" style="font-size: 10px;">z-index</a> <a href="/tags/原型链/" style="font-size: 10px;">原型链</a> <a href="/tags/同源/" style="font-size: 10px;">同源</a> <a href="/tags/外边距/" style="font-size: 10px;">外边距</a> <a href="/tags/层叠上下文/" style="font-size: 10px;">层叠上下文</a> <a href="/tags/性能优化/" style="font-size: 13.33px;">性能优化</a> <a href="/tags/数据结构/" style="font-size: 10px;">数据结构</a> <a href="/tags/浏览器/" style="font-size: 16.67px;">浏览器</a> <a href="/tags/百分比/" style="font-size: 10px;">百分比</a> <a href="/tags/算法/" style="font-size: 10px;">算法</a> <a href="/tags/精华/" style="font-size: 10px;">精华</a> <a href="/tags/软件下载/" style="font-size: 10px;">软件下载</a> <a href="/tags/鉴权/" style="font-size: 10px;">鉴权</a>
    </div>
  </div>

  
</aside>
        
      </div>
      <footer id="footer">
  
  <div class="outer">
    <div id="footer-info" class="inner">
      &copy; 2021 <a href="http://github.com/cai4633/cai4633.github.io" target="_blank">Cai4633</a><br>
      <!--Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>-->
    </div>
  </div>
</footer>
    </div>
    <nav id="mobile-nav">
  
    <a href="/" class="mobile-nav-link">Home</a>
  
    <a href="/archives" class="mobile-nav-link">Archives</a>
  
</nav>
    

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/2.0.3/jquery.min.js"></script>


  <link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
  <script src="/fancybox/jquery.fancybox.pack.js"></script>


<script src="/js/script.js"></script>



    <script>
      let isMobile = false
      if (/Android|webOS|iPhone|iPad|BlackBerry/i.test(navigator.userAgent)) {
        isMobile = true
        $("#nav").addClass("is-mobile")
        $("footer").addClass("is-mobile")
      }
      if ($(".local-search").size()) {
        $.getScript("/js/search.js", function () {
          searchFunc("/search.xml", "local-search-input", "local-search-result")
        })
      }
    </script>
  </div>
</body>
</html>